﻿/********************************************************
 *  ██████╗  ██████╗████████╗██╗
 * ██╔════╝ ██╔════╝╚══██╔══╝██║
 * ██║  ███╗██║        ██║   ██║
 * ██║   ██║██║        ██║   ██║
 * ╚██████╔╝╚██████╗   ██║   ███████╗
 *  ╚═════╝  ╚═════╝   ╚═╝   ╚══════╝
 * Geophysical Computational Tools & Library (GCTL)
 *
 * Copyright (c) 2023  Yi Zhang (yizhang-geo@zju.edu.cn)
 *
 * GCTL is distributed under a dual licensing scheme. You can redistribute 
 * it and/or modify it under the terms of the GNU Lesser General Public 
 * License as published by the Free Software Foundation, either version 2 
 * of the License, or (at your option) any later version. You should have 
 * received a copy of the GNU Lesser General Public License along with this 
 * program. If not, see <http://www.gnu.org/licenses/>.
 * 
 * If the terms and conditions of the LGPL v.2. would prevent you from using 
 * the GCTL, please consider the option to obtain a commercial license for a 
 * fee. These licenses are offered by the GCTL's original author. As a rule, 
 * licenses are provided "as-is", unlimited in time for a one time fee. Please 
 * send corresponding requests to: yizhang-geo@zju.edu.cn. Please do not forget 
 * to include some description of your company and the realm of its activities. 
 * Also add information on how to contact you by electronic and paper mail.
 ******************************************************/

#include "glni.h"

using namespace gctl;

double tet_volume(double x1, double x2, double x3, double x4, 
	double y1, double y2, double y3, double y4, 
	double z1, double z2, double z3, double z4)
{
	double x12 = x2-x1, y12 = y2-y1, z12 = z2-z1;
	double x13 = x3-x1, y13 = y3-y1, z13 = z3-z1;
	double x14 = x4-x1, y14 = y4-y1, z14 = z4-z1;
	double crx = y12*z13-z12*y13;
	double cry = z12*x13-x12*z13;
	double crz = x12*y13-y12*x13;
	double vol = (x14*crx+y14*cry+z14*crz)/6.0;
	if (vol < 0) vol = -1.0*vol;
	return vol;
}

glni::coefficients::coefficients()
{
    size_ = 0;
    nodes_ = nullptr;
    weights_ = nullptr;
}

glni::coefficients::coefficients(size_t size)
{
    initiate(size);
}

glni::coefficients::~coefficients()
{
    clear();
}

void glni::coefficients::initiate(size_t size)
{
#ifdef GCTL_CHECK_SIZE
    if (size == 0 || size > 20)
    {
        throw invalid_argument("Invalid initiating order. From glni::coefficients::initiate(...).");
    }
#endif

    size_ = size;
    nodes_ = new double [size_];
    weights_ = new double [size_];

    switch (size_)
    {
    case 1:
        nodes_[0] = 0.0;
        weights_[0] = 2.0;
        break;
    case 2:
        nodes_[0] = -0.577350269189625764509148780502; nodes_[1] = -1.0*nodes_[0];
        weights_[0] = weights_[1] = 1.0;
        break;
    case 3:
        nodes_[0] = -0.774596669241483377035853079956; nodes_[2] = -1.0*nodes_[0];
        nodes_[1] = 0.0;
        weights_[0] = weights_[2] = 0.5555555555555555555555555555565;
        weights_[1] = 0.8888888888888888888888888888889;
        break;
    case 4:
        nodes_[0] = -0.861136311594052575223946488893; nodes_[3] = -1.0*nodes_[0];
        nodes_[1] = -0.339981043584856264802665759103; nodes_[2] = -1.0*nodes_[1];
        weights_[0] = weights_[3] = 0.347854845137453857373063949222;
        weights_[1] = weights_[2] = 0.652145154862546142626936050778;
        break;
    case 5:
        nodes_[0] = -0.906179845938663992797626878299; nodes_[4] = -1.0*nodes_[0];
        nodes_[1] = -0.538469310105683091036314420700; nodes_[3] = -1.0*nodes_[1];
        nodes_[2] = 0.0;
        weights_[0] = weights_[4] = 0.236926885056189087514264040720;
        weights_[1] = weights_[3] = 0.478628670499366468041291514836;
        weights_[2] = 0.568888888888888888888888888889;
        break;
    case 6:
        nodes_[0] = -0.932469514203152027812301554494; nodes_[5] = -1.0*nodes_[0];
        nodes_[1] = -0.661209386466264513661399595020; nodes_[4] = -1.0*nodes_[1];
        nodes_[2] = -0.238619186083196908630501721681; nodes_[3] = -1.0*nodes_[2];
        weights_[0] = weights_[5] = 0.171324492379170345040296142173;
        weights_[1] = weights_[4] = 0.360761573048138607569833513838;
        weights_[2] = weights_[3] = 0.467913934572691047389870343990;
        break;
    case 7:
        nodes_[0] = -0.949107912342758524526189684048; nodes_[6] = -1.0*nodes_[0];
        nodes_[1] = -0.741531185599394439863864773281; nodes_[5] = -1.0*nodes_[1];
        nodes_[2] = -0.405845151377397166906606412077; nodes_[4] = -1.0*nodes_[2];
        nodes_[3] = 0.0;
        weights_[0] = weights_[6] = 0.129484966168869693270611432679;
        weights_[1] = weights_[5] = 0.279705391489276667901467771424;
        weights_[2] = weights_[4] = 0.381830050505118944950369775489;
        weights_[3] = 0.417959183673469387755102040816;
        break;
    case 8:
        nodes_[0] = -0.960289856497536231683560868569; nodes_[7] = -1.0*nodes_[0];
        nodes_[1] = -0.796666477413626739591553936476; nodes_[6] = -1.0*nodes_[1];
        nodes_[2] = -0.525532409916328985817739049189; nodes_[5] = -1.0*nodes_[2];
        nodes_[3] = -0.183434642495649804939476142360; nodes_[4] = -1.0*nodes_[3];
        weights_[0] = weights_[7] = 0.101228536290376259152531354310;
        weights_[1] = weights_[6] = 0.222381034453374470544355994426;
        weights_[2] = weights_[5] = 0.313706645877887287337962201987;
        weights_[3] = weights_[4] = 0.362683783378361982965150449277;
        break;
    case 9:
        nodes_[0] = -0.968160239507626089835576202904; nodes_[8] = -1.0*nodes_[0];
        nodes_[1] = -0.836031107326635794299429788070; nodes_[7] = -1.0*nodes_[1];
        nodes_[2] = -0.613371432700590397308702039341; nodes_[6] = -1.0*nodes_[2];
        nodes_[3] = -0.324253423403808929038538014643; nodes_[5] = -1.0*nodes_[3];
        nodes_[4] = 0.0;
        weights_[0] = weights_[8] = 0.0812743883615744119718921581105;
        weights_[1] = weights_[7] = 0.180648160694857404058472031243;
        weights_[2] = weights_[6] = 0.260610696402935462318742869419;
        weights_[3] = weights_[5] = 0.312347077040002840068630406584;
        weights_[4] = 0.330239355001259763164525069287;
        break;
    case 10:
        nodes_[0] = -0.973906528517171720077964012084; nodes_[9] = -1.0*nodes_[0];
        nodes_[1] = -0.865063366688984510732096688423; nodes_[8] = -1.0*nodes_[1];
        nodes_[2] = -0.679409568299024406234327365115; nodes_[7] = -1.0*nodes_[2];
        nodes_[3] = -0.433395394129247290799265943166; nodes_[6] = -1.0*nodes_[3];
        nodes_[4] = -0.148874338981631210884826001130; nodes_[5] = -1.0*nodes_[4];
        weights_[0] = weights_[9] = 0.0666713443086881375935688098933;
        weights_[1] = weights_[8] = 0.149451349150580593145776339658;
        weights_[2] = weights_[7] = 0.219086362515982043995534934228;
        weights_[3] = weights_[6] = 0.269266719309996355091226921569;
        weights_[4] = weights_[5] = 0.295524224714752870173892994651;
        break;
    case 11:
        nodes_[0]=-0.978228658146056992803938001123; nodes_[10] = -1.0*nodes_[0];
        nodes_[1]=-0.887062599768095299075157769304; nodes_[9] = -1.0*nodes_[1];
        nodes_[2]=-0.730152005574049324093416252031; nodes_[8] = -1.0*nodes_[2];
        nodes_[3]=-0.519096129206811815925725669459; nodes_[7] = -1.0*nodes_[3];
        nodes_[4]=-0.269543155952344972331531985401; nodes_[6] = -1.0*nodes_[4];
        nodes_[5]= 0.0;
        weights_[0]= weights_[10] = 0.0556685671161736664827537204425;
        weights_[1]= weights_[9] = 0.125580369464904624634694299224;
        weights_[2]= weights_[8] = 0.186290210927734251426097641432;
        weights_[3]= weights_[7] = 0.233193764591990479918523704843;
        weights_[4]= weights_[6] = 0.262804544510246662180688869891;
        weights_[5]= 0.272925086777900630714483528336;
        break;
    case 12:
        nodes_[0]=-0.981560634246719250690549090149; nodes_[11] = -1.0*nodes_[0];
        nodes_[1]=-0.904117256370474856678465866119; nodes_[10] = -1.0*nodes_[1];
        nodes_[2]=-0.769902674194304687036893833213; nodes_[9] = -1.0*nodes_[2];
        nodes_[3]=-0.587317954286617447296702418941; nodes_[8] = -1.0*nodes_[3];
        nodes_[4]=-0.367831498998180193752691536644; nodes_[7] = -1.0*nodes_[4];
        nodes_[5]=-0.125233408511468915472441369464; nodes_[6] = -1.0*nodes_[5];
        weights_[0]= weights_[11] = 0.0471753363865118271946159614850;
        weights_[1]= weights_[10] = 0.106939325995318430960254718194;
        weights_[2]= weights_[9] = 0.160078328543346226334652529543;
        weights_[3]= weights_[8] = 0.203167426723065921749064455810;
        weights_[4]= weights_[7] = 0.233492536538354808760849898925;
        weights_[5]= weights_[6] = 0.249147045813402785000562436043;
        break;
    case 13:
        nodes_[0]=-0.984183054718588149472829448807; nodes_[12] = -1.0*nodes_[0];
        nodes_[1]=-0.917598399222977965206547836501; nodes_[11] = -1.0*nodes_[1];
        nodes_[2]=-0.801578090733309912794206489583; nodes_[10] = -1.0*nodes_[2];
        nodes_[3]=-0.642349339440340220643984606996; nodes_[9] = -1.0*nodes_[3];
        nodes_[4]=-0.448492751036446852877912852128; nodes_[8] = -1.0*nodes_[4];
        nodes_[5]=-0.230458315955134794065528121098; nodes_[7] = -1.0*nodes_[5];
        nodes_[6]= 0.0;
        weights_[0]= weights_[12] = 0.0404840047653158795200215922010;
        weights_[1]= weights_[11] = 0.0921214998377284479144217759538;
        weights_[2]= weights_[10] = 0.138873510219787238463601776869;
        weights_[3]= weights_[9] = 0.178145980761945738280046691996;
        weights_[4]= weights_[8] = 0.207816047536888502312523219306;
        weights_[5]= weights_[7] = 0.226283180262897238412090186040;
        weights_[6]= 0.232551553230873910194589515269;
        break;
    case 14:
        nodes_[0]=-0.986283808696812338841597266704; nodes_[13] = -1.0*nodes_[0];
        nodes_[1]=-0.928434883663573517336391139378; nodes_[12] = -1.0*nodes_[1];
        nodes_[2]=-0.827201315069764993189794742650; nodes_[11] = -1.0*nodes_[2];
        nodes_[3]=-0.687292904811685470148019803019; nodes_[10] = -1.0*nodes_[3];
        nodes_[4]=-0.515248636358154091965290718551; nodes_[9] = -1.0*nodes_[4];
        nodes_[5]=-0.319112368927889760435671824168; nodes_[8] = -1.0*nodes_[5];
        nodes_[6]=-0.108054948707343662066244650220; nodes_[7] = -1.0*nodes_[6];
        weights_[0]= weights_[13] = 0.0351194603317518630318328761382;
        weights_[1]= weights_[12] = 0.0801580871597602098056332770629;
        weights_[2]= weights_[11] = 0.121518570687903184689414809072;
        weights_[3]= weights_[10] = 0.157203167158193534569601938624;
        weights_[4]= weights_[9] = 0.185538397477937813741716590125;
        weights_[5]= weights_[8] = 0.205198463721295603965924065661;
        weights_[6]= weights_[7] = 0.215263853463157790195876443316;
        break;
    case 15:
        nodes_[0]=-0.987992518020485428489565718587; nodes_[14] = -1.0*nodes_[0];
        nodes_[1]=-0.937273392400705904307758947710; nodes_[13] = -1.0*nodes_[1];
        nodes_[2]=-0.848206583410427216200648320774; nodes_[12] = -1.0*nodes_[2];
        nodes_[3]=-0.724417731360170047416186054614; nodes_[11] = -1.0*nodes_[3];
        nodes_[4]=-0.570972172608538847537226737254; nodes_[10] = -1.0*nodes_[4];
        nodes_[5]=-0.394151347077563369897207370981; nodes_[9] = -1.0*nodes_[5];
        nodes_[6]=-0.201194093997434522300628303395; nodes_[8] = -1.0*nodes_[6];
        nodes_[7]= 0.0;
        weights_[0]= weights_[14] = 0.0307532419961172683546283935772;
        weights_[1]= weights_[13] = 0.0703660474881081247092674164507;
        weights_[2]= weights_[12] = 0.107159220467171935011869546686;
        weights_[3]= weights_[11] = 0.139570677926154314447804794511;
        weights_[4]= weights_[10] = 0.166269205816993933553200860481;
        weights_[5]= weights_[9] = 0.186161000015562211026800561866;
        weights_[6]= weights_[8] = 0.198431485327111576456118326444;
        weights_[7]= 0.202578241925561272880620199968;
        break;
    case 16:
        nodes_[0]=-0.989400934991649932596154173450; nodes_[15] = -1.0*nodes_[0];
        nodes_[1]=-0.944575023073232576077988415535; nodes_[14] = -1.0*nodes_[1];
        nodes_[2]=-0.865631202387831743880467897712; nodes_[13] = -1.0*nodes_[2];
        nodes_[3]=-0.755404408355003033895101194847; nodes_[12] = -1.0*nodes_[3];
        nodes_[4]=-0.617876244402643748446671764049; nodes_[11] = -1.0*nodes_[4];
        nodes_[5]=-0.458016777657227386342419442984; nodes_[10] = -1.0*nodes_[5];
        nodes_[6]=-0.281603550779258913230460501460; nodes_[9] = -1.0*nodes_[6];
        nodes_[7]=-0.0950125098376374401853193354250; nodes_[8] = -1.0*nodes_[7];
        weights_[0]= weights_[15] = 0.0271524594117540948517805724560;
        weights_[1]= weights_[14] = 0.0622535239386478928628438369944;
        weights_[2]= weights_[13] = 0.0951585116824927848099251076022;
        weights_[3]= weights_[12] = 0.124628971255533872052476282192;
        weights_[4]= weights_[11] = 0.149595988816576732081501730547;
        weights_[5]= weights_[10] = 0.169156519395002538189312079030;
        weights_[6]= weights_[9] = 0.182603415044923588866763667969;
        weights_[7]= weights_[8] = 0.189450610455068496285396723208;
        break;
    case 17:
        nodes_[0]=-0.990575475314417335675434019941; nodes_[16] = -1.0*nodes_[0];
        nodes_[1]=-0.950675521768767761222716957896; nodes_[15] = -1.0*nodes_[1];
        nodes_[2]=-0.880239153726985902122955694488; nodes_[14] = -1.0*nodes_[2];
        nodes_[3]=-0.781514003896801406925230055520; nodes_[13] = -1.0*nodes_[3];
        nodes_[4]=-0.657671159216690765850302216643; nodes_[12] = -1.0*nodes_[4];
        nodes_[5]=-0.512690537086476967886246568630; nodes_[11] = -1.0*nodes_[5];
        nodes_[6]=-0.351231763453876315297185517095; nodes_[10] = -1.0*nodes_[6];
        nodes_[7]=-0.178484181495847855850677493654; nodes_[9] = -1.0*nodes_[7];
        nodes_[8]= 0.0;
        weights_[0]= weights_[16] = 0.0241483028685479319601100262876;
        weights_[1]= weights_[15] = 0.0554595293739872011294401653582;
        weights_[2]= weights_[14] = 0.0850361483171791808835353701911;
        weights_[3]= weights_[13] = 0.111883847193403971094788385626;
        weights_[4]= weights_[12] = 0.135136368468525473286319981702;
        weights_[5]= weights_[11] = 0.154045761076810288081431594802;
        weights_[6]= weights_[10] = 0.168004102156450044509970663788;
        weights_[7]= weights_[9] = 0.176562705366992646325270990113;
        weights_[8]= 0.179446470356206525458265644262;
        break;
    case 18:
        nodes_[0]=-0.991565168420930946730016004706; nodes_[17] = -1.0*nodes_[0];
        nodes_[1]=-0.955823949571397755181195892930; nodes_[16] = -1.0*nodes_[1];
        nodes_[2]=-0.892602466497555739206060591127; nodes_[15] = -1.0*nodes_[2];
        nodes_[3]=-0.803704958972523115682417455015; nodes_[14] = -1.0*nodes_[3];
        nodes_[4]=-0.691687043060353207874891081289; nodes_[13] = -1.0*nodes_[4];
        nodes_[5]=-0.559770831073947534607871548525; nodes_[12] = -1.0*nodes_[5];
        nodes_[6]=-0.411751161462842646035931793833; nodes_[11] = -1.0*nodes_[6];
        nodes_[7]=-0.251886225691505509588972854878; nodes_[10] = -1.0*nodes_[7];
        nodes_[8]=-0.0847750130417353012422618529358; nodes_[9] = -1.0*nodes_[8];
        weights_[0]= weights_[17] = 0.0216160135264833103133427102665;
        weights_[1]= weights_[16] = 0.0497145488949697964533349462026;
        weights_[2]= weights_[15] = 0.0764257302548890565291296776166;
        weights_[3]= weights_[14] = 0.100942044106287165562813984925;
        weights_[4]= weights_[13] = 0.122555206711478460184519126800;
        weights_[5]= weights_[12] = 0.140642914670650651204731303752;
        weights_[6]= weights_[11] = 0.154684675126265244925418003836;
        weights_[7]= weights_[10] = 0.164276483745832722986053776466;
        weights_[8]= weights_[9] = 0.169142382963143591840656470135;
        break;
    case 19:
        nodes_[0]=-0.992406843843584403189017670253; nodes_[18] = -1.0*nodes_[0];
        nodes_[1]=-0.960208152134830030852778840688; nodes_[17] = -1.0*nodes_[1];
        nodes_[2]=-0.903155903614817901642660928532; nodes_[16] = -1.0*nodes_[2];
        nodes_[3]=-0.822714656537142824978922486713; nodes_[15] = -1.0*nodes_[3];
        nodes_[4]=-0.720966177335229378617095860824; nodes_[14] = -1.0*nodes_[4];
        nodes_[5]=-0.600545304661681023469638164946; nodes_[13] = -1.0*nodes_[5];
        nodes_[6]=-0.464570741375960945717267148104; nodes_[12] = -1.0*nodes_[6];
        nodes_[7]=-0.316564099963629831990117328850; nodes_[11] = -1.0*nodes_[7];
        nodes_[8]=-0.160358645640225375868096115741; nodes_[10] = -1.0*nodes_[8];
        nodes_[9]= 0.0;
        weights_[0]= weights_[18] = 0.0194617882297264770363120414644;
        weights_[1]= weights_[17] = 0.0448142267656996003328381574020;
        weights_[2]= weights_[16] = 0.0690445427376412265807082580060;
        weights_[3]= weights_[15] = 0.0914900216224499994644620941238;
        weights_[4]= weights_[14] = 0.111566645547333994716023901682;
        weights_[5]= weights_[13] = 0.128753962539336227675515784857;
        weights_[6]= weights_[12] = 0.142606702173606611775746109442;
        weights_[7]= weights_[11] = 0.152766042065859666778855400898;
        weights_[8]= weights_[10] = 0.158968843393954347649956439465;
        weights_[9]= 0.161054449848783695979163625321;
        break;
    case 20:
        nodes_[0]=-0.993128599185094924786122388471; nodes_[19] = -1.0*nodes_[0];
        nodes_[1]=-0.963971927277913791267666131197; nodes_[18] = -1.0*nodes_[1];
        nodes_[2]=-0.912234428251325905867752441203; nodes_[17] = -1.0*nodes_[2];
        nodes_[3]=-0.839116971822218823394529061702; nodes_[16] = -1.0*nodes_[3];
        nodes_[4]=-0.746331906460150792614305070356; nodes_[15] = -1.0*nodes_[4];
        nodes_[5]=-0.636053680726515025452836696226; nodes_[14] = -1.0*nodes_[5];
        nodes_[6]=-0.510867001950827098004364050955; nodes_[13] = -1.0*nodes_[6];
        nodes_[7]=-0.373706088715419560672548177025; nodes_[12] = -1.0*nodes_[7];
        nodes_[8]=-0.227785851141645078080496195369; nodes_[11] = -1.0*nodes_[8];
        nodes_[9]=-0.0765265211334973337546404093988; nodes_[10] = -1.0*nodes_[9];
        weights_[0]= weights_[19] = 0.0176140071391521183118619623519;
        weights_[1]= weights_[18] = 0.0406014298003869413310399522749;
        weights_[2]= weights_[17] = 0.0626720483341090635695065351870;
        weights_[3]= weights_[16] = 0.0832767415767047487247581432220;
        weights_[4]= weights_[15] = 0.101930119817240435036750135480;
        weights_[5]= weights_[14] = 0.118194531961518417312377377711;
        weights_[6]= weights_[13] = 0.131688638449176626898494499748;
        weights_[7]= weights_[12] = 0.142096109318382051329298325067;
        weights_[8]= weights_[11] = 0.149172986472603746787828737002;
        weights_[9]= weights_[10] = 0.152753387130725850698084331955;
        break;
    }
    return;
}

void glni::coefficients::clear()
{
    size_ = 0;
    if (nodes_ != nullptr) delete[] nodes_;
    if (weights_ != nullptr) delete[] weights_;
    nodes_ = nullptr;
    weights_ = nullptr;
    return;
}

double glni::coefficients::node(size_t idx)
{
#ifdef GCTL_CHECK_SIZE
    if (idx >= size_)
    {
        throw out_of_range("Invalid index. From gctl::glni::coefficients::node(...)");
    }
#endif

    return nodes_[idx];
}

double glni::coefficients::weight(size_t idx)
{
#ifdef GCTL_CHECK_SIZE
    if (idx >= size_)
    {
        throw out_of_range("Invalid index. From gctl::glni::coefficients::weight(...)");
    }
#endif

    return weights_[idx];
}


glni::line::line()
{
    initialized_ = false;
    size_ = 0;
}

glni::line::line(size_t size)
{
    initiate(size);
}

glni::line::~line()
{
    clear();
}

void glni::line::clear()
{
    coeff_.clear();
    initialized_ = false;
    size_ = 0;
    return;
}

void glni::line::initiate(size_t size)
{
#ifdef GCTL_CHECK_SIZE
    if (size == 0 || size > 20)
    {
        throw invalid_argument("Invalid initiating order. From glni::line::initiate(...)");
    }
#endif

    initialized_ = true;
    size_ = size;
    coeff_.initiate(size_);
    return;
}

double glni::line::integral(double low, double hig, void *att)
{
#ifdef GCTL_CHECK_BOUNDER
    if (hig <= low)
    {
        throw length_error("Invalid integration range. From glni::line::integral(...)");
    }
#endif

    if (!initialized_)
    {
        throw runtime_error("The object is not initialized. From gctl::glni::line::integral(...).");
    }

    double scl = (hig - low)/2.0;
    double gx, inte = 0.0;
    for (int i = 0; i < size_; i++)
    {
        gx = 0.5*(low + hig) + scl*coeff_.node(i);
        inte += coeff_.weight(i)*scl*line_func(gx, low, hig, att);
    }
    return inte;
}


glni::quadrilateral::quadrilateral()
{
    initialized_ = false;
    size_ = 0;
}

glni::quadrilateral::quadrilateral(size_t size)
{
    initiate(size);
}

glni::quadrilateral::~quadrilateral()
{
    clear();
}

void glni::quadrilateral::clear()
{
    initialized_ = false;
    size_ = 0;
    coeff_.clear();
    return;
}

void glni::quadrilateral::initiate(size_t size)
{
#ifdef GCTL_CHECK_SIZE
    if (size == 0 || size > 20)
    {
        throw invalid_argument("Invalid initiating order. From glni::quadrilateral::initiate(...)");
    }
#endif

    initialized_ = true;
    size_ = size;
    coeff_.initiate(size_);
    return;
}

double glni::quadrilateral::integral(double xlow, double xhig, double ylow, double yhig, void *att)
{
#ifdef GCTL_CHECK_BOUNDER
    if (xhig <= xlow || yhig <= ylow)
    {
        throw length_error("Invalid integration range. From glni::quadrilateral::integral(...)");
    }
#endif

    if (!initialized_)
    {
        throw runtime_error("Un-initialized. From glni::quadrilateral::integral(...)");
    }

    double scl = (xhig-xlow)*(yhig-ylow)/4.0;
    double gx, gy, inte = 0.0;
    for (int i = 0; i < size_; i++)
    {
        for (int j = 0; j < size_; j++)
        {
            gx = 0.5*(xlow+xhig) + 0.5*(xhig-xlow)*coeff_.node(j);
            gy = 0.5*(ylow+yhig) + 0.5*(yhig-ylow)*coeff_.node(i);
            inte += coeff_.weight(i)*coeff_.weight(j)*scl*quadrilateral_func(gx, gy, xlow, xhig, ylow, yhig, att);
        }
    }
    return inte;
}


glni::triangle::triangle()
{
    initialized_ = false;
    size_ = 0;
}

glni::triangle::triangle(size_t size)
{
    initiate(size);
}

glni::triangle::~triangle()
{
    clear();
}

void glni::triangle::clear()
{
    initialized_ = false;
    size_ = 0;
    coeff_.clear();
    return;
}

void glni::triangle::initiate(size_t size)
{
#ifdef GCTL_CHECK_SIZE
    if (size == 0 || size > 20)
    {
        throw invalid_argument("Invalid initiating order. From glni::triangle::initiate(...)");
    }
#endif

    initialized_ = true;
    size_ = size;
    coeff_.initiate(size_);
    return;
}

double glni::triangle::integral(double x1, double x2, double x3, double y1, 
    double y2, double y3, void *att)
{
#ifdef GCTL_CHECK_BOUNDER
    if ((x2-x1)*(y3-y1)-(y2-y1)*(x3-x1) <= GCTL_ZERO) // 这里没有设置为0是为了检测三个点是否共线
    {
        throw length_error("Invalid integration range. From glni::triangle::integral(...)");
    }
#endif

    if (!initialized_)
    {
        throw runtime_error("Un-initialized. From glni::triangle::integral(...)");
    }

    double ksi, eta;
    double gx, gy, inte = 0.0;
    double scl = x1*y2+x2*y3+x3*y1-x1*y3-x2*y1-x3*y2;
    for (int i = 0; i < size_; i++)
    {
        for (int j = 0; j < size_; j++)
        {
            ksi = 0.5*(1.0+coeff_.node(j));
            eta = 0.25*(1.0-coeff_.node(j))*(1.0+coeff_.node(i));
            gx = (1.0-ksi-eta)*x1 + ksi*x2 + eta*x3;
            gy = (1.0-ksi-eta)*y1 + ksi*y2 + eta*y3;
            inte += coeff_.weight(i)*coeff_.weight(j)*scl*triangle_func(gx, gy, x1, x2, x3, y1, y2, y3, att)
                *(1.0-coeff_.node(j))*0.125;
        }
    }
    return inte;
}


glni::triangle_c::triangle_c()
{
    initialized_ = false;
    size_ = 0;
}

glni::triangle_c::triangle_c(size_t size)
{
    initiate(size);
}

glni::triangle_c::~triangle_c()
{
    clear();
}

void glni::triangle_c::clear()
{
    initialized_ = false;
    size_ = 0;
    coeff_.clear();
    return;
}

void glni::triangle_c::initiate(size_t size)
{
#ifdef GCTL_CHECK_SIZE
    if (size == 0 || size > 20)
    {
        throw invalid_argument("Invalid initiating order. From glni::triangle_c::initiate(...)");
    }
#endif

    initialized_ = true;
    size_ = size;
    coeff_.initiate(size_);
    return;
}

std::complex<double> glni::triangle_c::integral(double x1, double x2, double x3, double y1, 
    double y2, double y3, void *att)
{
#ifndef GCTL_CHECK_BOUNDER
    if ((x2-x1)*(y3-y1)-(y2-y1)*(x3-x1) <= GCTL_ZERO) // 这里没有设置为0是为了检测三个点是否共线
    {
        throw length_error("Invalid integration range. From glni::triangle::integral(...)");
    }
#endif

    if (!initialized_)
    {
        throw runtime_error("Un-initialized. From glni::triangle::integral(...)");
    }

	double ksi, eta;
	double gx, gy;
	std::complex<double> inte = std::complex<double>(0.0, 0.0);
	double scl = x1*y2+x2*y3+x3*y1-x1*y3-x2*y1-x3*y2;
	for (int i = 0; i < size_; i++)
	{
		for (int j = 0; j < size_; j++)
		{
			ksi = 0.5*(1.0+coeff_.node(j));
			eta = 0.25*(1.0-coeff_.node(j))*(1.0+coeff_.node(i));
			gx = (1.0-ksi-eta)*x1 + ksi*x2 + eta*x3;
			gy = (1.0-ksi-eta)*y1 + ksi*y2 + eta*y3;
			inte += coeff_.weight(i)*coeff_.weight(j)*scl*triangle_c_func(gx, gy, x1, x2, x3, y1, y2, y3, att)
				*(1.0-coeff_.node(j))*0.125;
		}
	}
	return inte;
}


glni::cube::cube()
{
    initialized_ = false;
    size_ = 0;
}

glni::cube::cube(size_t size)
{
    initiate(size);
}

glni::cube::~cube()
{
    clear();
}

void glni::cube::clear()
{
    initialized_ = false;
    size_ = 0;
    coeff_.clear();
    return;
}

void glni::cube::initiate(size_t size)
{
#ifdef GCTL_CHECK_SIZE
    if (size == 0 || size > 20)
    {
        throw invalid_argument("Invalid initiating order. From glni::cube::initiate(...)");
    }
#endif

    initialized_ = true;
    size_ = size;
    coeff_.initiate(size_);
    return;
}

double glni::cube::integral(double x1, double x2, double y1, double y2, 
    double z1, double z2, void *att)
{
#ifdef GCTL_CHECK_BOUNDER
    if (x2 <= x1 || y2 <= y1 || z2 <= z1)
    {
        throw length_error("Invalid integration range. From glni::cube::integral(...)");
    }
#endif

    if (!initialized_)
    {
        throw runtime_error("Un-initialized. From glni::cube::integral(...)");
    }

    double gx, gy, gz, inte = 0.0;
    double scl = (x2-x1)*(y2-y1)*(z2-z1)/8.0;

    for (int i = 0; i < size_; i++)
    {
        for (int j = 0; j < size_; j++)
        {
            for (int k = 0; k < size_; k++)
            {
                gx = 0.5*(x1+x2) + 0.5*(x2-x1)*coeff_.node(k);
                gy = 0.5*(y1+y2) + 0.5*(y2-y1)*coeff_.node(j);
                gz = 0.5*(z1+z2) + 0.5*(z2-z1)*coeff_.node(i);
                inte += coeff_.weight(i)*coeff_.weight(j)*coeff_.weight(k)*scl
                    *cube_func(gx, gy, gz, x1, x2, y1, y2, z1, z2, att);
            }
        }
    }
    return inte;
}


glni::tetrahedron::tetrahedron()
{
    initialized_ = false;
    size_ = 0;
}

glni::tetrahedron::tetrahedron(size_t size)
{
    initiate(size);
}

glni::tetrahedron::~tetrahedron()
{
    clear();
}

void glni::tetrahedron::clear()
{
    initialized_ = false;
    size_ = 0;
    coeff_.clear();
    return;
}

void glni::tetrahedron::initiate(size_t size)
{
#ifdef GCTL_CHECK_SIZE
    if (size == 0 || size > 20)
    {
        throw invalid_argument("Invalid initiating order. From glni::tetrahedron::initiate(...)");
    }
#endif

    initialized_ = true;
    size_ = size;
    coeff_.initiate(size_);
    return;
}

double glni::tetrahedron::integral(double x1, double x2, double x3, double x4, 
    double y1, double y2, double y3, double y4, 
    double z1, double z2, double z3, double z4, 
    void *att)
{
    if (!initialized_)
    {
        throw runtime_error("Un-initialized. From glni::tetrahedron::integral(...)");
    }

    double ksi, eta, zta;
    double inte = 0.0;
    double scl = 6.0*tet_volume(x1, x2, x3, x4, y1, y2, y3, y4, z1, z2, z3, z4);

	if (scl < GCTL_ZERO)
    {
        throw std::invalid_argument("glni::tetrahedron::integral(...) got an invalid integration range.");
    }

    for (int i = 0; i < size_; i++)
    {
        for (int j = 0; j < size_; j++)
        {
            for (int k = 0; k < size_; k++)
            {
                ksi = 0.125*(1.0+coeff_.node(k))*(1.0+coeff_.node(j))*(1.0+coeff_.node(i));
                eta = 0.125*(1.0+coeff_.node(k))*(1.0+coeff_.node(j))*(1.0-coeff_.node(i));
                zta = 0.25*(1.0+coeff_.node(k))*(1.0-coeff_.node(j));
                inte += coeff_.weight(i)*coeff_.weight(j)*coeff_.weight(k)*scl
                    *tetrahedron_func(ksi, eta, zta, x1, x2, x3, x4, y1, y2, y3, y4, z1, z2, z3, z4, att)
                    *(1.0+coeff_.node(k))*(1.0+coeff_.node(k))*(1.0+coeff_.node(j))/64.0;
            }
        }
    }
    return inte;
}


glni::triprism::triprism()
{
	initialized_ = false;
	size_ = 0;
}

glni::triprism::triprism(size_t size)
{
	initiate(size);
}

glni::triprism::~triprism()
{
	clear();
}

void glni::triprism::clear()
{
	initialized_ = false;
	size_ = 0;
	coeff_.clear();
	return;
}

void glni::triprism::initiate(size_t size)
{
#ifdef GCTL_CHECK_SIZE
	if (size == 0 || size > 20)
    {
		throw std::invalid_argument("glni::triprism::initiate(...) got an invalid initiating order.");
    }
#endif // GCTL_CHECK_BOUNDER

	initialized_ = true;
	size_ = size;
	coeff_.initiate(size_);
	return;
}

double glni::triprism::integral(double *xs, double *ys, double *zs, void *att)
{
	if (!initialized_)
    {
        throw std::invalid_argument("glni::triprism is not initialized.");
    }

	double vol = 0.0;
	for (size_t i = 0; i < 3; i++)
	{
		vol += tet_volume(xs[i], xs[i+1], xs[i+2], xs[i+3], 
			ys[i], ys[i+1], ys[i+2], ys[i+3], zs[i], zs[i+1], zs[i+2], zs[i+3]);
	}

	double scl = 2.0*vol;
	double ksi, eta, zta;
	double inte = 0.0;
	for (int i = 0; i < size_; i++)
	{
		for (int j = 0; j < size_; j++)
		{
			for (int k = 0; k < size_; k++)
			{
				ksi = 0.5*(1.0+coeff_.node(j));
				eta = 0.25*(1.0-coeff_.node(j))*(1.0+coeff_.node(i));
				zta = 0.5*(1.0+coeff_.node(k));
				
				inte += coeff_.weight(i)*coeff_.weight(j)*coeff_.weight(k)*scl
					*triprism_func(ksi, eta, zta, xs, ys, zs, att)
					*(1.0-coeff_.node(j))*0.0625;
			}
		}
	}
	return inte;
}
